{-# LANGUAGE TypeOperators #-}
module Analysis.Abstract.Tracing
( tracingTerms
, tracing
) where

import Control.Abstract hiding (trace)
import Control.Effect.Writer
import Data.Semigroup.Reducer as Reducer

-- | Trace analysis.
--
--   Instantiating @trace@ to @[]@ yields a linear trace analysis, while @Set@ yields a reachable state analysis.
tracingTerms :: ( Member (State (Heap address address value)) sig
               , Member (Writer (trace (Configuration term address value))) sig
               , Carrier sig m
               , Reducer (Configuration term address value) (trace (Configuration term address value))
               )
             => trace (Configuration term address value)
             -> Open (term -> Evaluator term address value m a)
tracingTerms proxy recur term = getConfiguration term >>= trace . (`asTypeOf` proxy) . Reducer.unit >> recur term

trace :: ( Member (Writer (trace (Configuration term address value))) sig
        , Carrier sig m
        )
      => trace (Configuration term address value)
      -> Evaluator term address value m ()
trace = tell

tracing :: (Monoid (trace (Configuration term address value)))
        => Evaluator term address value (WriterC (trace (Configuration term address value)) (Evaluator term address value m)) a
        -> Evaluator term address value m (trace (Configuration term address value), a)
tracing = runWriter . runEvaluator


-- | Get the current 'Configuration' with a passed-in term.
getConfiguration :: (Member (State (Heap address address value)) sig, Carrier sig m)
                 => term
                 -> Evaluator term address value m (Configuration term address value)
getConfiguration term = Configuration term <$>  getHeap

-- | A single point in a program’s execution.
data Configuration term address value = Configuration
  { configurationTerm :: term                       -- ^ The “instruction,” i.e. the current term to evaluate.
  , configurationHeap :: Heap address address value -- ^ The heap of values.
  }
  deriving (Eq, Ord, Show)
